/**
 * 参考https://github.com/joewalnes/reconnecting-websocket/blob/master/reconnecting-websocket.js
 * 使用方式：
 *  创建自动连接webSocket
 *  const ws = new ReconnectingWebsocket({
 *     wsUrl: 'ws://localhost:8081/broadcast/websocket',
 *     resolveMessageType: (data)=>String, //配置如何解析消息类型
       resolveMessageBody: (data) =>Object,//配置如何解析消息体
 *  })
 * 
 *  //配置事件类型与绑定的函数
 *  ws.run(eventType|function, function({type, data, message}){    })
 *  
 * @author xiufu.wang
 */

function transFormHandleByMode(mode, handle, ws) {
    if (!mode || mode === 'strict') {
        return handle
    }
    if (mode === 'debounce') {
        return (function (debounceInterval, cb) {
            let timer = null;
            return function (...arg) {
                clearTimeout(timer)
                timer = setTimeout(function () {
                    cb(...arg)
                }, debounceInterval);
            }
        })(ws.debounceInterval, handle)
    }

    if (mode === 'space') {
        return (function (spaceInterval, cb) {
            let queues = [];
            let interval = null;
            return function (...arg) {
                queues.unshift([...arg]);
                if (!interval) {
                    interval = setInterval(function () {
                        const funcArgs = queues.pop()
                        cb(...funcArgs);
                        if (queues.length === 0) {
                            clearInterval(interval);
                            interval = null;
                        }
                    }, spaceInterval);
                }
            }
        })(ws.spaceInterval, handle)
    }
}

//构造函数
export default function ReconnectingWebsocket(options) {
    this.reconnectAttempts = 0;
    this.readyState = WebSocket.CONNECTING;
    const self = this;
    let ws;
    let forcedClose = false;
    let timedOut = false;

    const settings = {
        /**
         * 是否开启debug模式, 若开启，则控制台会输出相关日志信息
         */
        debug: false,
        /**
         * 当new PlotVmWebSocket时，是否自定开启webSocket
         */
        automaticOpen: true,
        /**
         * 首次打开web应用时, 当前地址只有匹配scopeUrlPrefix前缀的才会，启用当前PlotVmWebSocket
         */
        scopeUrlPrefix: null,
        /**
         * 当前webSocket的地址
         */
        wsUrl: null,
        /**
         * 业务名称字段配置
        */
        name: 'anonymous-pot-vm-websocket',
        /**
         * 提醒模式(strict|space|debounce) 
         *  strict: 只要接收到消息,立即提示
         *  space: 当连续接收到多条消息时, 会按照固定的频率(spaceInterval)提示
         *  debounce: 当接收到多条消息时, 在debounceInterval内,仅会触发一次
        */
        mode: null,
        /**
         * 当mode = space时,该配置有效
        */
        spaceInterval: 10000,
        /**
         * 当mode = debounce时,该配置有效
        */
        debounceInterval: 3000,

        resolveMessageType: data => null,
        resolveMessageBody: data => data,

    }
    // 内部private配置
    const privateSettings = {
        /**
         * blob
         */
        binaryType: 'blob',
        /**
         * 重新连接超时时间
         */
        timeoutInterval: 2000,
        /**
         * 重连延迟时间
         */
        reconnectInterval: 6000,

        pingInterval: 30000
    }

    let tasks = [];

    if (!options) { options = {}; }

    for (var key in settings) {
        if (typeof options[key] !== 'undefined') {
            this[key] = options[key];
        } else {
            this[key] = settings[key];
        }
    }

    for (var key in privateSettings) {
        this[key] = privateSettings[key];
    }

    function invorkMatcherTasks(message) {
        const type = self.resolveMessageType(message);
        const data = self.resolveMessageBody(message);
        if (type === false) {
            return
        }

        for (let i = 0; i < tasks.length; i++) {
            const t = tasks[i]
            const _type = t[0];
            const _cb = t[1];
            if (_type === null || _type === undefined || _type === type || (typeof _type === 'function' && _type(type, data, message) === true)) {
                _cb({ type, data, message, name: self.name })
            }
        }
    }

    //打开ws
    this.open = function (reconnectAttempt) {
        const scopeUrlPrefix = (self.scopeUrlPrefix || '').trim();
        const pathname = window.location.pathname.toLocaleLowerCase()
        if (scopeUrlPrefix && !pathname.startsWith(scopeUrlPrefix.toLocaleLowerCase())) {
            return;
        }

        ws = new WebSocket(self.wsUrl);
        ws.binaryType = this.binaryType;
        if (reconnectAttempt) {
            if (self.debug) {
                console.debug(`PlotVmWebSocket[${self.name}]`, `重新连接:第${this.reconnectAttempts}次`, self.wsUrl);
            }
        } else {
            this.reconnectAttempts = 0;
            if (self.debug) {
                console.debug(`PlotVmWebSocket[${self.name}]`, '开始连接', self.wsUrl);
            }
        }

        //处理超时逻辑
        const localWs = ws;
        const timer = setTimeout(() => {
            if (self.debug) {
                console.debug(`PlotVmWebSocket[${self.name}]`, `连接超时`, self.wsUrl);
            }
            timedOut = true;
            localWs.close();
            timedOut = false;
        }, self.timeoutInterval)

        let pollingPingIntervaler = null;

        ws.onopen = function (event) {
            //连接成功之后，清除超时处理逻辑
            clearTimeout(timer)
            if (self.debug) {
                console.debug(`PlotVmWebSocket[${self.name}]`, '连接成功', self.wsUrl);
            }
            self.protocol = ws.protocol;
            self.readyState = WebSocket.OPEN;
            self.reconnectAttempts = 0;

            pollingPingIntervaler = setInterval(function () {
                if (ws) {
                    if (self.debug) {
                        console.debug(`PlotVmWebSocket[${self.name}]`, 'ping...', self.wsUrl);
                    }
                    try {
                        ws.send(`{"type": "ping"}`)
                    } catch (error) {
                        if (self.debug) {
                            console.error(`PlotVmWebSocket[${self.name}]`, 'ping...', self.wsUrl);
                        }
                    }
                }
            }, self.pingInterval)
        }

        //关闭
        ws.onclose = function (event) {
            clearTimeout(timer);
            clearInterval(pollingPingIntervaler)
            ws = null;
            if (forcedClose) {
                self.reconnectAttempts = 0;
                if (self.debug) {
                    console.debug(`PlotVmWebSocket[${self.name}]`, '关闭', self.wsUrl);
                }
                self.readyState = WebSocket.CLOSED;
            } else {
                self.readyState = WebSocket.CONNECTING;
                //重新连接
                setTimeout(() => {
                    self.reconnectAttempts++;
                    self.open(true);
                }, self.reconnectInterval)
            }
        }

        ws.onmessage = function (event) {
            if (self.debug) {
                console.debug(`PlotVmWebSocket[${self.name}]`, `接收到消息`, self.wsUrl, event.data);
            }
            try {
                invorkMatcherTasks(JSON.parse(event.data))
            } catch (error) {
                if (self.debug) {
                    console.debug(`PlotVmWebSocket[${self.name}]`, `JSON.parse转换失败`, self.wsUrl, event.data);
                }
            }
        };
        ws.onerror = function (event) {
            if (self.debug) {
                console.debug(`PlotVmWebSocket[${self.name}]`, `发生错误`, self.wsUrl, event);
            }
        };
    }

    //自动打开ws
    if (this.automaticOpen) {
        this.open(false)
    }

    //强制关闭
    this.close = function (code, reason) {
        forcedClose = true;
        tasks = [];
        if (ws) {
            ws.close(1000, '关闭');
        }
    }

    this.run = function (type, cb) {
        const n = [type, transFormHandleByMode(self.mode, cb, self)]
        tasks.push(n);
        return () => {
            tasks.splice( tasks.indexOf(n), 1)
        }
    }
}

